/*
	节点面板
*/
define(
	[
		'require',
		'zrender/tool/util',
		'zrender/Group',
		'zrender/shape/Base',
		'zrender/shape/Image',
		'zrender/shape/Line',
		'zrender/shape/Text',
		'zrender/shape/Rectangle',
		'zrender/shape/Circle',
		'zrender/shape/Ring',
		'zrender/shape/Polyline',
		'zrender/shape/Polygon',
		'./component',
		'./menus'
	],
	function(require) {
		"use strict";

		var BaseShape = require('zrender/shape/Base');
		var ImageShape = require('zrender/shape/Image');
		var LineShape = require('zrender/shape/Line');
		var RectangleShape = require('zrender/shape/Rectangle');
		var CircleShape = require('zrender/shape/Circle');
		var RingShape = require('zrender/shape/Ring');
		var PolylineShape = require('zrender/shape/Polyline');
		var TextShape = require('zrender/shape/Text');
		var PolygonShape = require('zrender/shape/Polygon');
		var Group = require('zrender/Group');

		var zrUtil = require('zrender/tool/util');

		var Component = require('./component');
		var Menus = require('./menus');

		var forEach = Ember.EnumerableUtils.forEach;


		var requestAnimationFrame = window.requestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || function(func) {
			return setTimeout(func, 16);
		};

		var defaultNodeIconUrl = "resource/img/topo/icon_node.png";
		var itemWidth = 65 * 0.85,
			itemPadding = 10;

		function substrByteLen(str, start, byteLen) {
			if (str == undefined || start == undefined) {
				return;
			}

			if (byteLen == undefined) {
				return str.substr(start);
			}

			var i = start,
				l = str.length,
				curByteLen = 0;
			for (; i < l; i++) {
				if (str.substr(i, 1).match(/[^\x00-\xff]/ig) != null) {
					curByteLen += 2;
				} else {
					curByteLen += 1;
				}
				if (curByteLen >= byteLen) {
					break;
				}
			}
			return str.substr(start, i + 1);
		}
		/*
			节点面板中用到的指标项, 标题加数字
		*/
		var PerformanceItem = Component.extend({
			width: itemWidth,

			clickable: false,

			render: function() {
				var g = this.group,
					zlevel = this.zlevel;

				this.valueColor = '#ffffff';

				g.position[0] = this.x;
				g.position[1] = this.y+8;

				g.addChild(new TextShape({
					clickable: this.clickable,
					hoverable: false,
					Z: zlevel,
					style: {
						x: 0,
						y: 0,
						maxWidth: this.width,
						color: this.valueColor,
						text: (this.value === undefined) ? "-" : this.value,
						textFont: '15px Arial'
					}
				}));

				g.addChild(new TextShape({
					hoverable: false,
					Z: zlevel,
					style: {
						x: 0,
						y: 18,
						maxWidth: this.width,
						color: "#6ac5fe",
						text: this.title,
						textFont: '11px Arial'
					}
				}));

				this._addEventListner();
			},

			_addEventListner: function() {
				var me = this,
					g = this.group,
					runBind = Ember.run.bind;

				g.bind("click", runBind(this, "_click"));
			},

			_click: function(e) {
				e.dataKey = this.dataKey;
				e.xtype = "pfmsItemClick";
			},

			updateValueImmediately: function(newText, valueColor) {
				var zr = this.zr,
					txtShapeId = this.group.childAt(0).id;

				this.value = newText;

				if (this.animator) {
					this.animator.stop();
					this.animator = null;
				}

				zr.modElement(txtShapeId, {
					style: {
						y: 0,
						opacity: 1,
						text: newText,
						color: valueColor || this.valueColor
					}
				});
			},

			updateValueAnimatedly: function(newText, valueColor) {
				var me = this,
					zr = me.zr,
					txtShapeId = me.group.childAt(0).id;

				if (me.animator) {
					this.updateValueImmediately(newText,valueColor);
					return;
				}

				this.value = newText;

				me.animator = zr.animate(txtShapeId, "style", false)
					.when(300, {
						y: -30,
						opacity: 0
					})
					.start();

				me.animator.done(function() {
					zr.modElement(txtShapeId, {
						style: {
							y: 30,
							opacity: 0,
							text: newText,
							color: valueColor || me.valueColor
						}
					});
					me.animator = zr.animate(txtShapeId, "style", false)
						.when(300, {
							y: 0,
							opacity: 1
						})
						.done(function() {
							me.animator = null;
						})
						.start();
				});
			}
		});

		/*
			节点面板中用到的简易折线图
		*/
		var SimpleBrokenLine = Component.extend({
			width: itemWidth * 2,

			height: 30,

			render: function() {
				if (!this.data) {
					return;
				}

				var me = this;
				var maxLen = 60,
					width = this.width,
					height = this.height,
					zlevel = this.zlevel;
				var g = this.group;

				g.position[0] = this.x;
				g.position[1] = this.y;

				var pointList = this._getPointList();

				forEach(pointList, function(subPointList) {
					var PolygonPointList = [];
					if (subPointList.length > 0) {
						PolygonPointList = subPointList.concat([
							[subPointList[subPointList.length - 1][0], height],
							[subPointList[0][0], height]
						]);
					}

					g.addChild(new PolygonShape({
						Z: zlevel,
						style: {
							pointList: PolygonPointList,
							color: 'rgb(138,237,119)'
						},
						hoverable: false
					}));
					g.addChild(new PolylineShape({
						Z: zlevel,
						style: {
							pointList: subPointList,
							strokeColor: 'rgb(202,255,186)',
							lineWidth: 1
						},
						hoverable: false
					}));
				});

				var index = this.indexAt(this.get("timePoint"));
				this.redline = new LineShape({
					position: [width / maxLen * index, 0],
					Z: zlevel + 2,
					style: {
						xStart: 0,
						yStart: -2,
						xEnd: 0,
						yEnd: height,
						strokeColor: '#ff0000',
						lineWidth: 1
					},
					hoverable: false
				});
				g.addChild(this.redline);
			},

			timePointChange: Ember.observer("timePoint", function() {
				if (!this.data) {
					return;
				}
				var zr = this.zr;
				var maxLen = 60,
					width = this.width;
				var index = this.indexAt(this.get("timePoint"));
				this.zr.modElement(this.redline.id, {
					position: [width / maxLen * index, 0]
				});
			}),

			dataChange: Ember.observer("data", function() {
				this.group.clearChildren();
				this._performacesMap = null;

				this.render();
			}),

			performanceAt: function(timePoint) {
				if (!this._performacesMap) {
					var _performacesMap = this._performacesMap = {};
					forEach(this.data, function(performance, i) {
						_performacesMap[performance.timestamp] = {
							data: performance,
							index: i
						};
					});
				}
				var p = this._performacesMap[timePoint];
				return (p && p.data) || {};
			},

			indexAt: function(timePoint) {
				this.performanceAt(timePoint);
				var p = this._performacesMap[timePoint];
				var index = p && p.index;
				index = index === undefined ? (this.data.length - 1) : index;
				return index;
			},

			_getPointList: function() {
				var maxLen = 60,
					width = this.width,
					height = this.height,
					data = this.data || [],
					dataKey = this.dataKey;

				var i = -1;
				var pointList = [],
					tmpPointList, pos;
				var maxValue = 0,
					dataKey = this.dataKey;
				forEach(data, function(item) {
					if (item[dataKey] > maxValue) {
						maxValue = item[dataKey];
					}
				});
				while (i++ < Math.min(data.length - 1, maxLen - 1)) {
					var curVal = data[i][dataKey],
						preVal, nextVal;
					if (i > 0) {
						preVal = data[i - 1][dataKey];
					}
					if (i < data.length - 1) {
						nextVal = data[i + 1][dataKey];
					}
					if (preVal === undefined) {
						tmpPointList = [];
						pointList.push(tmpPointList);
					}
					pos = [width / maxLen * i, height - (height * data[i][dataKey] / maxValue)];
					tmpPointList.push(pos);
				}
				return pointList;
			}
		});

		//节点面板组件
		var NodePanel = Component.extend({
			displayMode: "full",

			draggable: true,

			width: (itemWidth + itemPadding) * 3,

			height: 75,

			minHeight: 75 * 0.8,

			maxHeight: 75,

			displayModeChange: Ember.observer("displayMode", function() {
				var me = this,
					zr = this.zr,
					g = this.group;

				switch (me.displayMode) {
					case "small":
						this.height = me.minHeight;
						break;
					case "full":
					default:
						this.height = me.maxHeight;
						break;
				}

				if (this.data.nodeType == 4) {
					this.height = 180;
				}
				if (this.resizeAnimator) {
					this.resizeAnimator.stop();
				}
				this.data.size = this.get("displayMode");

				//TODO: 尺寸变化动画效果
				_resize();

				function _resize() {
					me.bgGroup.eachChild(function(shape) {
						zr.modElement(shape, {
							style: {
								height: me.height
							}
						});
					});
					me.stackG.eachChild(function(shape) {
						zr.modElement(shape, {
							style: {
								height: me.height
							}
						});
					});
					me._resize();
					zr.refreshNextFrame();
				}
			}),

			render: function() {
				var g = this.group;

				g.position[0] = this.x;
				g.position[1] = this.y;

				this._renderBg();

				this._renderHeader();

				this._renderComps();

				this._renderMenus();

				this._addEventListner();

				this._resize();

				this.displayModeChange();
			},

			_renderBg: function() {
				var stackG = new Group();
				var bgG = new Group();
				var g = this.group;

				//面板背景
				var defaultConfig = {
					hoverable: false,
					draggable: false,
					Z: 0,
					style: {
						x: 0,
						y: 0,
						color: 'rgb(24,120,199)',
						width: this.width,
						height: this.height,
						radius: 10,
						strokeColor: 'rgba(106,197,254,0.4)',
						lineWidth: 1
					},
					highlightStyle: {
						strokeColor: 'rgba(106,197,254,0.4)'
					}
				};

				var bgConfig = zrUtil.clone(defaultConfig);
				var bgMaskConfig = zrUtil.clone(defaultConfig);

				zrUtil.merge(bgConfig, {
					brushTypeOnly: "both",
					style: {
						strokeColor: 'rgba(106,197,254,0.4)'
					}
				}, true);

				zrUtil.merge(bgMaskConfig, {
					clickable: false,
					draggable: !!this.draggable,
					Z: 4,
					style: {
						color: "transparent"
					},
					highlightStyle: {
						lineWidth: 1
					},
					onmouseover: function() {}
				}, true);

				if (this.data.nodeType == 2 && !this.isExpanded) {
					var overlayLen = Math.min(5, this.data.nodeIds.length);

					var childConfig = {
						brushTypeOnly: "both",
						clickable: true,
						hoverable: false,
						draggable: false,
						Z: 0,
						style: {
							x: 0,
							y: 0,
							color: 'rgba(0,0,0,0)',
							strokeColor: 'rgba(106,197,254,0.6)',
							width: this.width,
							height: this.height,
							radius: 10,
							lineWidth: 1
						}
					};
					for (var i = overlayLen; i > 0; i--) {
						stackG.addChild(new RectangleShape(zrUtil.merge(zrUtil.clone(childConfig), {
							style: {
								x: i * 3,
								y: i * (-3)
							}
						}, true)));
					}
				}
				if (this.data.nodeType == 4) {
					zrUtil.merge(bgMaskConfig, {
						style: {
							width: this.width,
							height: this.height
						}
					}, true);
					zrUtil.merge(bgConfig, {
						style: {
							width: this.width,
							height: this.height
						}
					}, true);
				}
				var bgShape = new RectangleShape(bgConfig);
				var bgMaskShape = new RectangleShape(bgMaskConfig);
				bgG.addChild(stackG);
				bgG.addChild(bgShape);
				bgG.addChild(bgMaskShape);
				g.addChild(bgG);

				this.bgGroup = bgG;
				this.stackG = stackG;
				this.bgShape = bgShape;
				this.bgMaskShape = bgMaskShape;
			},

			_renderHeader: function() {
				var headerG = new Group();
				var data = this.data;
				if (data.nodeType == 4) {
					//业务组件标题

					var regE = /^[0-9]+|[a-zA-Z]+$/;
					// var regN = /^\d+$/

					var title = data.name || "标题";

					var re = regE.test(title);

					if (re) {
						var textLength = (data.name.length) * 5;
					} else {
						var textLength = (data.name.length) * 9;
					}

					if (textLength >= 50) {
						textLength = 50;
					}


					var titleShape = new TextShape({
						hoverable: false,
						Z: 2,
						style: {
							x: this.width / 2 - textLength,
							y: 22,
							maxWidth: 100,
							text: substrByteLen(title, 0, 18),
							textFont: 'bold 18px Arial',
							color: '#fff'
						}
					});
					//业务组件图标
					var imgShape = new ImageShape({
						hoverable: false,
						Z: 2,
						style: {
							x: (this.width - 160) / 2,
							y: 50,
							width: 160,
							height: 102,
							image: data.picUrl || defaultNodeIconUrl
						}
					});

					headerG.addChild(titleShape);
					headerG.addChild(imgShape);
					this.group.addChild(headerG);
					this.subTitleShape = subTitleShape;
					this.imgBgShape = imgBgShape;
					return;
				}

				var statusShape = new RingShape({
					hoverable: false,
					Z: 2,
					style: {
						x: 40,
						y: 38,
						r0: 23,
						r: 25,
						color: 'rgb(26,161,77)',
					}
				});
				var imgBgShape = new CircleShape({
					hoverable: false,
					Z: 2,
					style: {
						x: 40,
						y: 38,
						r: 25,
						color: 'rgb(26,161,77)',
					}
				});
				//业务组件图标
				var imgShape = new ImageShape({
					hoverable: false,
					Z: 2,
					style: {
						x: 17,
						y: 15,
						width: 46,
						height: 46,
						image: data.iconUrl || defaultNodeIconUrl
					}
				});

				//业务组件标题
				var title = data.name || "标题";
				var titleShape = new TextShape({
					hoverable: false,
					Z: 2,
					style: {
						x: 78,
						y: 30,
						maxWidth: 100,
						text: substrByteLen(title, 0, 14),
						textFont: '14px Arial',
						color: '#ffffff'
					}
				});

				//业务组件副标题
				var subTitle = data.nodeType == 2 ? "共 " + data.nodeIds.length + " 台" : data.ip + ":" + data.port;
				var subTitleShape = new TextShape({
					hoverable: false,
					Z: 2,
					style: {
						x: 78,
						y: 52,
						maxWidth: 100,
						text: subTitle,
						textFont: '14px Arial',
						color: "#ffffff"
					}
				});

				headerG.addChild(statusShape);
				headerG.addChild(imgBgShape);
				headerG.addChild(imgShape);
				headerG.addChild(titleShape);
				headerG.addChild(subTitleShape);

				this.group.addChild(headerG);

				this.imgShape = imgShape;
				this.imgBgShape = imgBgShape;
				this.statusShape = statusShape;
				this.titleShape = titleShape;
				this.subTitleShape = subTitleShape;
			},

			//绘制吞吐量, 响应时间, 流量等
			_renderComps: function() {
				var bodyG = new Group();
				var me = this,
					indicatorConfig = this.data.indicatorConfig || [],
					compos = this.compos = Ember.A(),
					x = itemPadding,
					y = 90;

				this.bodyG = bodyG;
				this.group.addChild(bodyG);


				forEach(indicatorConfig, function(indicator) {
					if (x > itemWidth * 2 + itemPadding * 3) {
						x = itemPadding;
						y += 60;
					}

					var pItem = PerformanceItem.create({
						x: x,
						y: y,
						Z: 2,
						zr: me.zr,
						container: me,
						width: 65,
						groupName: "bodyG",
						dataKey: indicator.code,
						fmtType: indicator.transform,
						title: indicator.name,
						value: "-"
					});
					compos.push(pItem);
					pItem.render();

					x += itemWidth + itemPadding;

					if (indicator.mode == 2) {
						var sbl = SimpleBrokenLine.create({
							x: x,
							y: y,
							Z: 2,
							zr: me.zr,
							container: me,
							groupName: "bodyG",
							dataKey: indicator.code
						});
						compos.push(sbl);
						sbl.render();

						x = itemPadding;
						y += 60;
					}
				});

				this._updateMaxHeight();
			},

			_updateMaxHeight: function() {
				var lastComp = this.compos[this.compos.length - 1];
				if (lastComp) {

					this.maxHeight = (lastComp.y + 60) * 0.95;
				} else {
					this.maxHeight = this.minHeight;
				}
			},

			_formatData: function(type, val) {
				/*if(type == "transform_time_ms"){
					return APM.util.indicatorFormatter(type)(val).split("ms")[0];
				}*/
				return APM.util.indicatorFormatter(type)(val);
			},

			_getValueColor: function(performance, dataKey) {
				dataKey = dataKey + "_status";
				var statusMap = performance.statusMap || {};
				switch (statusMap[dataKey]) {
					case 1:
						return 'rgb(237,156,40)';
					case 2:
						return 'rgb(210,50,45)';
					default:
						return;
				}
			},

			_renderMenus: function() {
				var me = this;

				var menusConfig = Ember.A(this.menusConfig);
				var nodeType = this.data.nodeType;

				menusConfig = menusConfig.filter(function(config) {
					return (config.accessBy.indexOf(nodeType) != -1);
				});
				if (Ember.A(menusConfig).length == 0) {
					return;
				}
				if (Broada.getView("businessDetailGaugeTopo").get("auth.isNormal")) {
					menusConfig = Ember.A(menusConfig).filterBy("code", "SHOW_CLUSTER");
				}

				this.menus = Menus.create({
					auth: me.get("auth"),
					container: this.container.container.children[0],
					defaultBtn: {
						name: "查看详情",
						cls: "icon-book",
						code: "GOTO_DETAIL"
					},
					menusConfig: menusConfig,
					nodeType: nodeType,
					scaling: this.container.scaling,
					onItemClick: function(e, menuCode) {
						me.onMenuClick && me.onMenuClick(menuCode);
						me.trigger("menuClick", menuCode);
					}
				});
				this.menus.render();
			},

			_addEventListner: function() {
				var me = this,
					g = this.group,
					runBind = Ember.run.bind;

				g.bind("dragstart", runBind(this, "_dragstart"));
				g.bind("dragend", runBind(this, "_dragend"));
				g.bind("mouseover", runBind(this, "_mouseover"));
				g.bind("mouseout", runBind(this, "_mouseout"));
				g.bind("mousewheel", runBind(this, "_mousewheel"));
				g.bind("click", runBind(this, "_click"));
				this.stackG.bind("click", runBind(this, "_stackGclick"));
				this.stackG.bind("mouseover", runBind(this, "_stackGmouseover"));
				this.stackG.bind("mouseout", runBind(this, "_stackGmouseout"));
				//TODO: 拖动效果需要优化, 在放大和缩小状态下, 节点移动的速度,相对于鼠标移动速度, 分别会过快和过慢
			},

			_dragstart: function(e) {
				var zr = this.zr,
					g = this.group;

				this.isDragging = true;

				this.hideMenus();

				this.ondragstart && this.ondragstart(e);
				this.trigger('dragstart', e);
			},

			_dragend: function(e) {
				var zr = this.zr,
					g = this.group;

				this.isDragging = false;

				g.ignore = false;

				zr.render();
				this.showMenus(e);

				this.ondragend && this.ondragend(e);
				this.trigger('dragend', e);
			},

			_mouseover: function(e) {
				var g = this.group;

				if (this.isDragging || this.isHover || !g) {
					return;
				}

				this.isHover = true;

				this.onmouseenter && this.onmouseenter(e);
				this.trigger('mouseenter', e);
			},

			_mouseout: function(e) {
				var g = this.group,
					menus = this.menus,
					target = e.event.toElement;

				var isEnterDropDown = menus ? menus.isTargetInMe(target) : false;

				if (this.isDragging || !this.isHover || !g || isEnterDropDown) {
					return;
				}

				this.isHover = false;

				this.onmouseout && this.onmouseout(e);
				this.trigger('mouseout', e);
			},

			_mousewheel: function(e) {
				var me = this;
				requestAnimationFrame(function() {
					if (me.menus) {
						me._refreshMenuPosition();
					}
				});
			},

			_click: function(e) {
				e.targetNodePanel = this;
			},

			_stackGclick: function(e) {
				e.xtype = "stackClick";
			},

			_stackGmouseover: function(e) {
				e.xtype = "stackMouseover";
			},

			_stackGmouseout: function(e) {
				e.xtype = "stackMouseout";
			},

			hover: function() {
				var zr = this.zr;

				zr.modElement(this.bgShape.id, {
					style: {
						x: 0,
						y: 0,
						width: this.width,
						height: this.height,
						lineWidth: 3,
						brushType: 'both'
					}
				});
			},

			unhover: function() {
				var zr = this.zr;

				zr.modElement(this.bgShape.id, {
					style: {
						lineWidth: 1,
						brushType: 'fill'
					}
				});
			},

			hoverStack: function() {
				var zr = this.zr,
					overlayLen = Math.min(5, this.data.nodeIds.length);

				forEach(this.stackG._children, function(shape, i) {
					if (shape._unHoverStackAnimator) {
						shape._unHoverStackAnimator.stop();
						shape._unHoverStackAnimator = null;
						zr.modElement(shape.id, {
							style: {
								x: (overlayLen - i) * 5,
								y: (overlayLen - i) * -5
							}
						});
					}

					if (shape._hoverStackAnimator) {
						return;
					}

					shape._hoverStackAnimator = zr.animate(shape.id, "style", false)
						.when(100, {
							x: (overlayLen - i) * 5,
							y: (overlayLen - i) * -5
						})
						.done(function() {
							shape._hoverStackAnimator = null;
						})
						.start();
				});
				zr.render();
			},

			unhoverStack: function() {
				var zr = this.zr,
					overlayLen = Math.min(5, this.data.nodeIds.length);

				forEach(this.stackG._children, function(shape, i) {
					if (shape._hoverStackAnimator) {
						shape._hoverStackAnimator.stop();
						shape._hoverStackAnimator = null;
						zr.modElement(shape.id, {
							style: {
								x: (overlayLen - i) * 5,
								y: (overlayLen - i) * -5
							}
						});
					}

					if (shape._unHoverStackAnimator) {
						return;
					}

					shape._unHoverStackAnimator = zr.animate(shape.id, "style", false)
						.when(100, {
							x: (overlayLen - i) * 3,
							y: (overlayLen - i) * -3
						})
						.done(function() {
							shape._unHoverStackAnimator = null;
						})
						.start();
				});
				zr.render();
			},

			showMenus: function() {
				if (this.menus) {
					this._refreshMenuPosition();
					this.menus.show();
				}
			},

			hideMenus: function() {
				this.menus && this.menus.hide();
			},

			largenBorder: function() {
				var zr = this.zr;

				zr.animate(this.bgShape.id, "style", false)
					.when(300, {
						x: -5,
						y: -5,
						width: this.width + 10,
						height: this.height + 10
					})
					.start();
			},

			resumeBorder: function() {
				var zr = this.zr;

				zr.modElement(this.bgShape.id, {
					style: {
						x: 0,
						y: 0,
						width: this.width,
						height: this.height
					}
				});
			},

			_resize: function() {
				switch (this.displayMode) {
					case "small":
						this.bodyG.ignore = true;
						break;
					case "full":
					default:
						this.bodyG.ignore = false;
						break;
				}
			},

			draggableChange: Ember.observer("draggable", function() {
				var zr = this.zr;

				zr.modElement(this.bgMaskShape.id, {
					draggable: !!this.draggable
				});
			}),

			timePointChange: Ember.observer("timePoint", function() {
				var tp = this.get("timePoint"),
					pfmc = this.performanceAt(tp),
					fmd = this._formatData,
					gvc = this._getValueColor;

				this.compos.forEach(function(item,index) {
					if (item instanceof PerformanceItem) {
						item.updateValueAnimatedly(fmd(item.fmtType, pfmc[item.dataKey]), gvc(pfmc, item.dataKey))
					} else if (item instanceof SimpleBrokenLine) {
						item.set("timePoint", tp);
					}
				});

				this.updateHeader();
				this._playStatusAnimate();
			}),

			updatePerformances: function(performances) {
				//重新生成performacesMap
				this._performacesMap = null;
				this.data.performances = performances;

				var pfmc = this.performanceAt(this.timePoint),
					fmd = this._formatData,
					gvc = this._getValueColor;

				this.compos.forEach(function(item,index) {
					if (item instanceof PerformanceItem) {
						item.updateValueAnimatedly(fmd(item.fmtType, pfmc[item.dataKey]), gvc(pfmc, item.dataKey))
					} else if (item instanceof SimpleBrokenLine) {
						item.set("data", performances);
					}
				});

				this.updateHeader();
				this._playStatusAnimate();

				this._resize();
			},

			performanceAt: function(timePoint) {
				if (!this._performacesMap) {
					var _performacesMap = this._performacesMap = {};
					forEach(this.data.performances || [], function(performance) {
						_performacesMap[performance.timestamp] = performance;
					});
				}
				return this._performacesMap[timePoint] || {};
			},

			_refreshMenuPosition: function() {
				var menus = this.menus;
				var pos = this.container[this.groupName].position,
					layerPos = this.container.getLayerPos(),
					scaling = this.container.getScaling();

				var x = layerPos[0] + (pos[0] + this.x + this.width) * scaling - 5,
					y = layerPos[1] + (pos[1] + this.y) * scaling + 5;

				menus.position(x, y);
			},

			//移动节点面板同时移动菜单;
			position: function(x, y) {
				var me = this;
				var defer = this._super(x, y);

				defer.done(function() {
					if (me.menus) {
						me._refreshMenuPosition();
					}
				});

				this.data.x = x;
				this.data.y = y;

				return defer;
			},

			updateHeader: function() {
				var data = this.data,
					zr = this.zr,
					performance = this.performanceAt(this.get("timePoint")),
					imgUrl = data.iconUrl || defaultNodeIconUrl,
					color = 'rgb(94,209,72)';
				if (data.nodeType == 2) {
					imgUrl = "/web/resource/img/topo/node.png";
				}
				if (overviewOrCenterSwitch == "business") {
					var statusColor = performance.businessStatus;
				} else {
					var statusColor = performance.status;
				}
				switch (statusColor) {
					case 1:
						color = 'rgb(237,156,40)';
						break;
					case 2:
						color = 'rgb(210,50,45)';
						break;
					default:
						break;
				}

				zr.modElement(this.statusShape, {
					style: {
						color: color
					}
				});

				zr.modElement(this.imgBgShape, {
					style: {
						color: color
					}
				});

				zr.modElement(this.imgShape, {
					style: {
						image: imgUrl
					}
				});

				zr.modElement(this.titleShape, {
					style: {
						text: substrByteLen(data.name || "标题", 0, 14)
					}
				});

				var subTitle = data.nodeType == 2 ? "共 " + data.nodeIds.length + " 台" : data.ip + ":" + data.port;
				zr.modElement(this.subTitleShape, {
					style: {
						text: subTitle,
					}
				});
			},

			_playStatusAnimate: function() {
				var me = this,
					zr = this.zr,
					statusShape = me.statusShape,
					performance = this.performanceAt(this.get("timePoint"));
				if (performance.status == 1 || performance.status == 2) {
					if (me.statusAnimator) {
						return;
					}

					zr.modElement(statusShape, {
						style: {
							r0: 23,
							r: 25,
							opacity: 1
						}
					});

					me.statusAnimator = zr.animate(statusShape, "style", false)
						.delay(1000)
						.when(500, {
							r0: 33,
							r: 35,
							opacity: 0
						})
						.done(function() {
							me.statusAnimator = null;
							me._playStatusAnimate();
						})
						.start();
				} else {
					if (me.statusAnimator) {
						me.statusAnimator.stop();
						me.statusAnimator = null;
						zr.modElement(statusShape, {
							style: {
								r0: 23,
								r: 25,
								opacity: 1
							}
						});
					}
				}
			},

			destroy: function() {
				var g = this.group;
				g.unbind("dragstart");
				g.unbind("dragend");
				g.unbind("mouseover");
				g.unbind("mouseout");
				g.unbind("click");

				if (this.menus) {
					this.menus.destroy();
					this.menus = null;
				}
				this._super();
			}
		});

		return NodePanel;
	}
);